using System.Collections.Generic;
using Rubikev.Common;

namespace Rubikev.Rockface.Core.Entities
{
   public abstract class EntityBase : IEntity
   {
      /// <summary>
      /// The ID of the entity
      /// </summary>
      public virtual int Id { get; set; }

      #region Object Overrides

      /// <summary>
      /// Taken from #Architecture: 
      /// To help ensure hashcode uniqueness, a carefully selected random number multiplier 
      /// is used within the calculation.  Goodrich and Tamassia's Data Structures and
      /// Algorithms in Java asserts that 31, 33, 37, 39 and 41 will produce the fewest number
      /// of collissions.  See http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/
      /// for more information.
      /// </summary>
      public const int HashCodeMultiplier = 33;

      /// <summary>
      /// Gets the hash code of the entity
      /// </summary>
      /// <returns>The entity's hash code</returns>
      public override int GetHashCode()
      {
         int ret = GetType().GetHashCode();
         return (ret * HashCodeMultiplier) ^ Id.GetHashCode();
      }

      /// <summary>
      /// Compares the other objects for equality with this
      /// </summary>
      /// <param name="obj">The object to compare against</param>
      /// <returns>True if the objects are of the same type and have the same id</returns>
      public override bool Equals(object obj)
      {
         IEntity otherEntity = obj as IEntity;
         if (otherEntity == null)
         {
            return false;
         }

         if (ReferenceEquals(this, otherEntity))
         {
            return true;
         }

         return GetType().Equals(otherEntity.GetType()) && Id.Equals(otherEntity.Id);
      }

      #endregion

      #region Bi-directional Collection methods

      /// <summary>
      /// Adds the child entity to the collection, setting its reference property
      /// </summary>
      /// <typeparam name="TParent">The type of the parent entity</typeparam>
      /// <typeparam name="T">The type of entity in the collection</typeparam>
      /// <param name="collection">The collection to add the child to</param>
      /// <param name="toAdd">The child to add</param>
      /// <param name="parent">The parent of the child</param>
      protected void AddEntityToBiDirectionalCollection<T, TParent>(IList<T> collection, T toAdd, TParent parent) where T : IChildEntity<TParent> where TParent : IEntity
      {
         collection.Add(toAdd);
         toAdd.ParentEntity = parent;
      }

      /// <summary>
      /// Removes the child entity from the collection, resetting its reference property
      /// </summary>
      /// <typeparam name="T">The type of entity in the collection</typeparam>
      /// <typeparam name="TParent">The type of the parent entity</typeparam>
      /// <param name="collection">The collection to remove the child from</param>
      /// <param name="toRemove">The child to remove</param>
      protected void RemoveEntityFromBiDirectionalCollection<T, TParent>(ICollection<T> collection, T toRemove) where T : IChildEntity<TParent> where TParent : IEntity
      {
         toRemove.ParentEntity = default(TParent);
         collection.Remove(toRemove);
      }

      /// <summary>
      /// Clears the child entities from the collection, resetting each one's reference property
      /// </summary>
      /// <typeparam name="T">The type of entity in the collection</typeparam>
      /// <typeparam name="TParent">The type of the parent entity</typeparam>
      /// <param name="collection">The collection to clear</param>
      protected void ClearBiDirectionalAssociation<T, TParent>(IList<T> collection) where T : class, IChildEntity<TParent> where TParent : IEntity
      {
         foreach (var toRemove in collection)
         {
            toRemove.ParentEntity = default(TParent);
         }

         collection.Clear();
      }

      #endregion
   }
}